#ifndef _SFS_META_DATA_SERVER_H_
#define _SFS_META_DATA_SERVER_H_

#include <string>
#include <vector>
#include <map>
#include <set>

#include <log4cxx/logger.h>
#include <json/json.h>

#include "ServerFrameWork.h"
#include "CnfSvrClient.h"
#include "DataSvrClient.h"

#define WRITE_MD_FILE_MUTEX_NUM 1024

using std::string;
using std::vector;
using std::map;
using std::set;

using namespace log4cxx;


struct DATA_SVR_MD_RECORD {
	vector<uint32_t> crcs;
	int64_t file_size;
};

struct CLIENT_MD_RECORD {
	vector<uint32_t> crcs;
};

struct MD_FILE_INFO {
	map<int, DATA_SVR_MD_RECORD> data_svr_md_records;
	CLIENT_MD_RECORD client_md_record;
};

struct REPLICATE_TASK {
        int32_t dir_id;
        int64_t file_id;
        int32_t src_ds_id;
        int64_t src_file_size;
        vector<uint32_t> src_crcs;
	int32_t dst_ds_id;
        bool operator < (const REPLICATE_TASK & t) const {
                return (this->dir_id == t.dir_id ? (this->file_id < t.file_id) : (this->dir_id < t.dir_id));
        }
};
	
class MetadataSvr {

	public:
		int init(const char * log_conf_file, int my_mds_id, const char * cs_ip, unsigned short cs_port, \
				int cs_conn_pool_size, int cs_socket_conn_timeout, int cs_socket_send_timeout, \
				int cs_socket_recv_timeout, int cs_socket_max_response_size);

		int start();

	protected:
		static int handle_socket_input_func(int fd, void * arg);

		int dispatch_request(int cmd_id, json_object * j, string & response);

		int handle_create_md_file(json_object * j, string & response);

		int handle_delete_md_file(json_object * j, string & response);

		int handle_get_md_file_info(json_object * j, string & response);

		int handle_data_svr_notify_data_file_info(json_object * j, string & response);
		
		int handle_client_notify_client_data_info(json_object * j, string & response);
		
		int update_md_file_info_by_data_svr_notify(const char * file_path, int data_svr_id, vector<uint32_t> crcs, \
								int64_t file_size, int delete_flag);
		
		int handle_alloc_ds_4_new_file(json_object * j, string & response);
		
		int update_md_file_info_by_client_notify(const char * file_path, vector<uint32_t> crcs);
		
		int get_md_file_info(const char*, MD_FILE_INFO & info);
		
		int write_md_file_info(const char*, MD_FILE_INFO info );

		int check_replicate(MD_FILE_INFO info);
		int check_replicate(int32_t dir_id, int64_t file_id, MD_FILE_INFO info);
		int put_replicate_task_to_queue(REPLICATE_TASK task);
		int get_replicate_task_from_waiting_notify_queue(REPLICATE_TASK & task);
		int erase_replicate_task_from_waiting_response_queue(REPLICATE_TASK task);
		static void * notify_replicate_thread_routine(void * arg);

		int alloc_ds_4_replicate(set<int> ok_replica_ids);
		int acquire_write_md_file_lock(int32_t dir_id, int64_t file_id);
		int release_write_md_file_lock(int32_t dir_id, int64_t file_id);
		bool is_crcs_equal(vector<uint32_t> crcs1, vector<uint32_t> crcs2);
	private:
		static LoggerPtr _logger;
		string _listen_ip;
		unsigned short _listen_port;
		int _thread_num;
		string _log_conf_file;
		int _socket_idle_timeout;
		int _socket_send_timeout;
		int _socket_recv_timeout;
		int _max_request_size;
		string _root_data_path;
		int _my_mds_id;
		int _replicate_factor;
		int _min_modify_interval_before_replicate;
		set<REPLICATE_TASK> _replicate_waiting_notify_tasks;
		set<REPLICATE_TASK> _replicate_waiting_response_tasks_0;
		set<REPLICATE_TASK> _replicate_waiting_response_tasks_1;
		set<REPLICATE_TASK> _replicate_waiting_response_tasks_2;
		pthread_mutex_t _replicate_tasks_mutex;
		pthread_cond_t _replicate_tasks_cond;
		pthread_t _replicate_notify_thread;
		pthread_mutex_t _write_md_file_mutexes[WRITE_MD_FILE_MUTEX_NUM];

		ServerFrameWork _sfw;
		CnfSvrClient _csc;
		DataSvrClientGroup _dscg;

};

#endif
